package com.fowo.api.flow.service.impl;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fowo.api.common.model.R;
import com.fowo.api.flow.entity.ProcessTemplates;
import com.fowo.api.flow.entity.WorkFlow;
import com.fowo.api.flow.enums.flow.HandleTypeEnums;
import com.fowo.api.flow.model.ChildNode;
import com.fowo.api.flow.model.FormOperates;
import com.fowo.api.flow.model.SettingsInfo;
import com.fowo.api.flow.model.dto.*;
import com.fowo.api.flow.model.vo.*;
import com.fowo.api.flow.service.CallBackApprovalResults;
import com.fowo.api.flow.service.FlowService;
import com.fowo.api.flow.service.ProcessTemplateService;
import com.fowo.api.flow.utils.BpmnModelUtils;
import com.fowo.api.flow.utils.DingDingUtil;
import com.fowo.api.sys.component.ExecuteScriptEngine;
import com.fowo.api.sys.entity.SysUser;
import com.fowo.api.sys.service.SysUserService;
import com.fowo.api.user.model.CurrentUser;
import com.fowo.api.user.model.JwtUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.Process;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.CommentEntityImpl;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.engine.task.Attachment;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.fowo.api.flow.constant.CommonConstants.*;
import static com.fowo.api.flow.constant.NodeConstants.*;
import static com.fowo.api.flow.constant.WorkFlowConstants.PROCESS_PREFIX;

/**
 * @fileName: FlowServiceImpl
 * @Description: 代码目的，作用，如何工作
 * @Author: pzh
 * @Date: 2023/3/28 0028 17:51
 */
@Slf4j
@Service
public class FlowServiceImpl implements FlowService {

    @Resource
    private RepositoryService repositoryService;
    @Resource
    private ProcessTemplateService processTemplateService;

    @Resource
    private RuntimeService runtimeService;

    @Resource
    private HistoryService historyService;

    @Resource
    private TaskService taskService;

    @Resource
    private WorkFlowServiceImpl workFlowService;

    @Resource
    private SysUserService userService;

    @Resource
    private DingDingUtil dingDingUtil;

    @Resource
    private List<CallBackApprovalResults> callBackApprovalResults;

    @Resource
    private ExecuteScriptEngine scriptEngine;
    public static final String JSCONTENTFROM = "var data = ${data}; ${jsContent};; ";

    @Override
    public ProcessTemplates detail(String templateId) throws Exception {
        ProcessTemplates processTemplates = processTemplateService.getById(templateId);
        if (ObjectUtils.isEmpty(processTemplates)) {
            throw new Exception("模版不存在");
        }
        processTemplates.setLogo(processTemplates.getIcon());
        processTemplates.setFormId(processTemplates.getTemplateId());
        processTemplates.setFormName(processTemplates.getTemplateName());
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(PROCESS_PREFIX + templateId).latestVersion().singleResult();
        if (processDefinition == null) {
            throw new FlowableException("该流程暂未接入Flowable,请重试");
        }
        processTemplates.setProcessDefinitionId(processDefinition.getId());
        return processTemplates;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized R<Object> start(List<StartProcessInstanceDTO> startList) {

        R check = checkApprove(startList);
        if (!check.isSuccess()) {
            return check;
        }
        for (StartProcessInstanceDTO item : startList) {

            JSONObject formData = item.getFormData();
            CurrentUser startUserInfo = item.getStartUserInfo();
            Authentication.setAuthenticatedUserId(startUserInfo.getUserid());
            Map<String, Object> processVariables = new HashMap<>();
            processVariables.put(FORM_VAR, formData);
            processVariables.put(PROCESS_STATUS, BUSINESS_STATUS_1);
            processVariables.put(START_USER_INFO, JSONObject.toJSONString(startUserInfo));
            processVariables.put("root", startUserInfo.getUserid());
            Map formValue = JSONObject.parseObject(formData.toJSONString(), new TypeReference<Map>() {
            });
            processVariables.putAll(formValue);
            ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder();
            ProcessInstance processInstance = processInstanceBuilder
                    .processDefinitionId(item.getProcessDefinitionId())
                    .variables(processVariables)
                    .businessStatus(BUSINESS_STATUS_1)
                    .start();
            R<String> result = dingDingUtil.saveIntegratedInstance(item.getTemplateId(), item.getStartUserInfo().getUserid());
            if (!result.isSuccess()) {
                return R.fail(result.getErrorMessage());
            }
            String dingDingProcessInstanceId = result.getData();
            R<Long> integratedTask = dingDingUtil.createIntegratedTask(dingDingProcessInstanceId,
                    processInstance.getProcessInstanceId(), item.getFormName(), item.getRecordId());
            if (!integratedTask.isSuccess()) {
                return R.fail(integratedTask.getErrorMessage());
            }
            Long dingDingTaskId = integratedTask.getData();
            saveWorkFlow(item, processInstance, dingDingProcessInstanceId, dingDingTaskId);
            updateRecordStatus(item.getFormName(), item.getRecordId(), null,null);
        }
        return R.ok(true);
    }

    @Override
    public Page<HistoryProcessInstanceVO> applyList(ApplyDTO applyDTO) {
        List<HistoricProcessInstance> historicProcessInstances =
                historyService.createHistoricProcessInstanceQuery()
                        .includeProcessVariables()
                        .startedBy(applyDTO.getCurrentUserInfo().getUserid())
                        .orderByProcessInstanceStartTime().desc()
                        .listPage((applyDTO.getCurrent() - 1) * applyDTO.getPageSize(), applyDTO.getPageSize());
        long count = historyService.createHistoricProcessInstanceQuery()
                .startedBy(applyDTO.getCurrentUserInfo().getUserid()).count();
        List<HistoryProcessInstanceVO> historyProcessInstanceVOS = new ArrayList<>();
        Page<HistoryProcessInstanceVO> page = new Page<>();
        for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) {
            Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();
            HistoryProcessInstanceVO historyProcessInstanceVO = new HistoryProcessInstanceVO();
            historyProcessInstanceVO.setProcessInstanceId(historicProcessInstance.getId());
            historyProcessInstanceVO.setProcessDefinitionName(historicProcessInstance.getProcessDefinitionName());
            historyProcessInstanceVO.setStartUser(JSONObject.parseObject(MapUtil.getStr(processVariables, START_USER_INFO), new TypeReference<CurrentUser>() {
            }));
            historyProcessInstanceVO.setStartTime(historicProcessInstance.getStartTime());
            historyProcessInstanceVO.setEndTime(historicProcessInstance.getEndTime());
            Boolean flag = historicProcessInstance.getEndTime() == null ? false : true;
            historyProcessInstanceVO.setCurrentActivityName(getCurrentName(historicProcessInstance.getId(), flag, historicProcessInstance.getProcessDefinitionId()));
            historyProcessInstanceVO.setBusinessStatus(MapUtil.getStr(processVariables, PROCESS_STATUS));


            long totalTimes = historicProcessInstance.getEndTime() == null ?
                    (Calendar.getInstance().getTimeInMillis() - historicProcessInstance.getStartTime().getTime()) :
                    (historicProcessInstance.getEndTime().getTime() - historicProcessInstance.getStartTime().getTime());
            long dayCount = totalTimes / (1000 * 60 * 60 * 24);//计算天
            long restTimes = totalTimes % (1000 * 60 * 60 * 24);//剩下的时间用于计于小时
            long hourCount = restTimes / (1000 * 60 * 60);//小时
            restTimes = restTimes % (1000 * 60 * 60);
            long minuteCount = restTimes / (1000 * 60);

            String spendTimes = dayCount + "天" + hourCount + "小时" + minuteCount + "分";
            historyProcessInstanceVO.setDuration(spendTimes);
            historyProcessInstanceVOS.add(historyProcessInstanceVO);
        }
        page.setRecords(historyProcessInstanceVOS);
        page.setCurrent(applyDTO.getCurrent());
        page.setSize(applyDTO.getPageSize());
        page.setTotal(count);
        return page;
    }

    @Override
    public Page<TaskVO> toDoList(ApplyDTO taskDTO) {
        List<Task> tasks = taskService.createTaskQuery().taskAssignee(taskDTO.getCurrentUserInfo().getUserid())
                .includeProcessVariables()
                .orderByTaskCreateTime().desc()
                .listPage((taskDTO.getCurrent() - 1) * taskDTO.getPageSize(), taskDTO.getPageSize());
        long count = taskService.createTaskQuery().taskAssignee(taskDTO.getCurrentUserInfo().getUserid()).count();
        List<TaskVO> taskVOS = new ArrayList<>();
        Page<TaskVO> page = new Page<>();
        for (Task task : tasks) {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
            Map<String, Object> processVariables = task.getProcessVariables();
            TaskVO taskVO = new TaskVO();
            taskVO.setTaskId(task.getId());
            taskVO.setProcessInstanceId(task.getProcessInstanceId());
            taskVO.setProcessDefinitionName(bpmnModel.getMainProcess().getName());
            taskVO.setStartUser(JSONObject.parseObject(MapUtil.getStr(processVariables, START_USER_INFO), new TypeReference<CurrentUser>() {
            }));
            taskVO.setStartTime(historicProcessInstance.getStartTime());
            List<String> activeActivityIds = runtimeService.getActiveActivityIds(task.getProcessInstanceId());
            String activityId = activeActivityIds.get(0);
            FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(activityId);
            taskVO.setCurrentActivityName(flowElement.getName());
            taskVO.setBusinessStatus(MapUtil.getStr(processVariables, PROCESS_STATUS));
            taskVO.setTaskCreatedTime(task.getCreateTime());
            taskVOS.add(taskVO);
        }
        page.setRecords(taskVOS);
        page.setCurrent(taskDTO.getCurrent());
        page.setSize(taskDTO.getPageSize());
        page.setTotal(count);
        return page;
    }

    @Override
    public Page<TaskVO> doneList(ApplyDTO taskDTO) {
        List<HistoricTaskInstance> tasks = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(taskDTO.getCurrentUserInfo().getUserid())
                .includeProcessVariables()
                .orderByTaskCreateTime().desc()
                .listPage((taskDTO.getCurrent() - 1) * taskDTO.getPageSize(), taskDTO.getPageSize());
        long count = historyService.createHistoricTaskInstanceQuery()
                .taskAssignee(taskDTO.getCurrentUserInfo().getUserid()).count();
        List<TaskVO> taskVOS = new ArrayList<>();
        Page<TaskVO> page = new Page<>();
        for (HistoricTaskInstance task : tasks) {
            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult();
            Boolean flag = historicProcessInstance.getEndTime() == null ? false : true;
            BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
            Map<String, Object> processVariables = task.getProcessVariables();
            TaskVO taskVO = new TaskVO();
            taskVO.setTaskId(task.getId());
            taskVO.setProcessInstanceId(task.getProcessInstanceId());
            taskVO.setProcessDefinitionName(bpmnModel.getMainProcess().getName());
            taskVO.setStartUser(JSONObject.parseObject(MapUtil.getStr(processVariables, START_USER_INFO), new TypeReference<CurrentUser>() {
            }));
            taskVO.setStartTime(historicProcessInstance.getStartTime());
            taskVO.setCurrentActivityName(getCurrentName(task.getProcessInstanceId(), flag, task.getProcessDefinitionId()));
            taskVO.setBusinessStatus(MapUtil.getStr(processVariables, PROCESS_STATUS));
            taskVO.setEndTime(task.getEndTime());

            long totalTimes = task.getEndTime() == null ?
                    (Calendar.getInstance().getTimeInMillis() - task.getStartTime().getTime()) :
                    (task.getEndTime().getTime() - task.getStartTime().getTime());
            long dayCount = totalTimes / (1000 * 60 * 60 * 24);//计算天
            long restTimes = totalTimes % (1000 * 60 * 60 * 24);//剩下的时间用于计于小时
            long hourCount = restTimes / (1000 * 60 * 60);//小时
            restTimes = restTimes % (1000 * 60 * 60);
            long minuteCount = restTimes / (1000 * 60);
            String spendTimes = dayCount + "天" + hourCount + "小时" + minuteCount + "分";
            taskVO.setDuration(spendTimes);
            taskVOS.add(taskVO);
        }

        page.setRecords(taskVOS);
        page.setCurrent(taskDTO.getCurrent());
        page.setSize(taskDTO.getPageSize());
        page.setTotal(count);
        return page;
    }

    @Override
    public HandleDataVO instanceInfo(HandleDataDTO HandleDataDTO) {
        return buildInstanceInfo(HandleDataDTO.getProcessInstanceId(), HandleDataDTO.getTaskId());

    }

    @Override
    public HandleDataVO recordFlow(RecordFlowDTO recordFlowDTO) throws Exception {
        WorkFlow workFlow = workFlowService.queryWf(recordFlowDTO.getRecordId(), recordFlowDTO.getFormName());
        if (Objects.isNull(workFlow)) {
            throw new Exception("任务不存在！");
        }

        List<Task> task = taskService.createTaskQuery().processInstanceId(workFlow.getWfId()).list();
        return buildInstanceInfo(workFlow.getWfId(), CollectionUtils.isEmpty(task) ? null : task.get(0).getId());
    }

    @Override
    public R handle(HandleDataDTO handleDataDTO) {
        JwtUserInfo currentUserInfo = JwtUserInfo.fromHeader();
        if (Objects.isNull(currentUserInfo)) {
            return R.fail("当前登录人获取失败");
        }
        List<AttachmentDTO> attachments = handleDataDTO.getAttachments();
        String comments = handleDataDTO.getComments();
        JSONObject formData = handleDataDTO.getFormData();
        String taskId = handleDataDTO.getTaskId();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (Objects.isNull(task)) {
            return R.fail("任务不存在，或被其他处理");
        }
        if (!currentUserInfo.getUserId().toString().equals(task.getAssignee())) {
            return R.fail("暂无权限审批！，请联系管理员！");
        }
        Map<String, Object> map = new HashMap<>();
        if (formData != null && formData.size() > 0) {
            Map formValue = JSONObject.parseObject(formData.toJSONString(), new TypeReference<Map>() {
            });
            map.putAll(formValue);
            map.put(FORM_VAR, formData);
        }


        Authentication.setAuthenticatedUserId(String.valueOf(currentUserInfo.getUserId()));
        if (StringUtils.isNotBlank(comments)) {
            taskService.addComment(task.getId(), task.getProcessInstanceId(), HandleTypeEnums.getDescription(handleDataDTO.getHandleType()), comments);
        }
        if (attachments != null && attachments.size() > 0) {
            for (AttachmentDTO attachment : attachments) {
                taskService.createAttachment(HandleTypeEnums.getDescription(handleDataDTO.getHandleType()), taskId, task.getProcessInstanceId(), attachment.getName(), attachment.getName(), attachment.getUrl());
            }
        }

        if (StringUtils.isNotBlank(handleDataDTO.getSignInfo())) {
            taskService.addComment(task.getId(), task.getProcessInstanceId(), "sign", handleDataDTO.getSignInfo());
        }
        WorkFlow workFlow = workFlowService.getById(handleDataDTO.getProcessInstanceId());
        String result = "";

        HandleTypeEnums handleTypeEnums = HandleTypeEnums.of(handleDataDTO.getHandleType());
        switch (handleTypeEnums) {
            case AGREE:
                runtimeService.setVariables(task.getProcessInstanceId(), map);
                taskService.complete(task.getId());
                result = "AGREE";
                break;
            case REFUSE:
                map.put(PROCESS_STATUS, BUSINESS_STATUS_3);
                runtimeService.setVariables(task.getProcessInstanceId(), map);
                runtimeService.deleteProcessInstance(task.getProcessInstanceId(), "拒绝");
                result = "REFUSE";
                break;
            case REVOKE:
                if (!validateRootUser(handleDataDTO.getProcessInstanceId(), String.valueOf(currentUserInfo.getUserId()))) {
                    return R.fail("请发起人撤销");
                }
                map.put(PROCESS_STATUS, BUSINESS_STATUS_2);
                runtimeService.setVariables(task.getProcessInstanceId(), map);
                runtimeService.deleteProcessInstance(task.getProcessInstanceId(), "撤销");
                break;
            case ASSIGNEE:
                map.put(PROCESS_STATUS, BUSINESS_STATUS_1);
                runtimeService.setVariables(task.getProcessInstanceId(), map);
                taskService.setAssignee(taskId, handleDataDTO.getTransferUserInfo().getUserid());
                break;
            case ROLLBACK:
                runtimeService.setVariables(task.getProcessInstanceId(), map);
                runtimeService.createChangeActivityStateBuilder()
                        .processInstanceId(task.getProcessInstanceId())
                        .moveActivityIdTo(task.getTaskDefinitionKey(), handleDataDTO.getRollbackId())
                        .changeState();
                break;
            case ADD_MULTI:
                R r = validateMultiAddUser(handleDataDTO.getTaskId(), handleDataDTO.getMultiAddUserInfo().getUserid());
                if (r.isSuccess()) {
                    return R.fail("加签人重复");
                }
                map.put(PROCESS_STATUS, BUSINESS_STATUS_1);
                runtimeService.setVariables(task.getProcessInstanceId(), map);
                Map<String, Object> variableMap = new HashMap<>();
                variableMap.put("assigneeName", handleDataDTO.getMultiAddUserInfo().getUserid());
                ExecutionEntity execution = (ExecutionEntity) runtimeService.addMultiInstanceExecution(task.getTaskDefinitionKey(), task.getProcessInstanceId(), variableMap);
                break;
            case COMMENTS:
                map.put(PROCESS_STATUS, BUSINESS_STATUS_1);
                runtimeService.setVariables(task.getProcessInstanceId(), map);
                break;
            default:
                break;
        }

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(task.getProcessInstanceId()).includeProcessVariables().singleResult();
        Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();
        workFlow.setWfStatus(Integer.valueOf(MapUtil.getStr(processVariables, PROCESS_STATUS)));
        dingDingUtil.updateDingDingTask(handleDataDTO.getProcessInstanceId(), result);
        if (MapUtil.getStr(processVariables, PROCESS_STATUS).equals(BUSINESS_STATUS_1)) {
            R<Long> integratedTask = dingDingUtil.createIntegratedTask(workFlow.getProcessInstanceId(),
                    handleDataDTO.getProcessInstanceId(), workFlow.getFormName(), workFlow.getRecordId());
            if (!integratedTask.isSuccess()) {
                return integratedTask;
            }
            Long taskIdInfo = integratedTask.getData();
            workFlow.setTaskId(taskIdInfo);
        }

        workFlowService.updateById(workFlow);
        if (MapUtil.getStr(processVariables, PROCESS_STATUS).equals(BUSINESS_STATUS_3)
                || MapUtil.getStr(processVariables, PROCESS_STATUS).equals(BUSINESS_STATUS_4)) {
            List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().processInstanceId(historicProcessInstance.getId()).activityType("userTask").list();
            updateRecordStatus(workFlow.getFormName(), workFlow.getRecordId(), Integer.valueOf(MapUtil.getStr(processVariables, PROCESS_STATUS)),list);
        }
        return R.ok(true);
    }

    @Override
    public void deleteMulti(List<String> executionIds) {
        for (String executionId : executionIds) {
            runtimeService.deleteMultiInstanceExecution(executionId, true);
        }
    }

    @Override
    public List<MultiVO> queryMultiUsersInfo(Map<String, Object> map) {

        // 1、查询任务
        String taskId = MapUtil.getStr(map, "taskId");
        List<Task> list = queryTask(taskId);
        // 2、构建返回数据

        return buildMultiData(list, taskId);
    }

    @Override
    public List<NodeVo> queryNode(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId)
                .includeProcessVariables().singleResult();
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().processInstanceId(historicProcessInstance.getId()).activityType("userTask").list();
        List<NodeVo> nodeVos = new ArrayList<>();
        list.stream().forEach(item -> {
            if (item.getEndTime() != null && !"root".equals(item.getActivityId())) {
                NodeVo nodeVo = new NodeVo();
                nodeVo.setNodeId(item.getActivityId());
                nodeVo.setNodeName(item.getActivityName());
                nodeVos.add(nodeVo);
            }
        });

        return nodeVos;
    }

    @Override
    public Object callJsData(CallJsDataDTO callJsDataDTO) {
        if (StrUtil.isEmpty(callJsDataDTO.getDataProcessing())) {
            return callJsDataDTO.getData();
        }
        String replace = JSCONTENTFROM.replace("${data}", JSON.toJSONString(callJsDataDTO.getData(), SerializerFeature.WriteMapNullValue).replace("${jsContent}", callJsDataDTO.getDataProcessing()));
        Object result = null;
        try {
            result = scriptEngine.callJs(replace, callJsDataDTO.getArgs(), null, callJsDataDTO.getService());
        } catch (Exception ex) {
            Assert.isTrue(false, "执行JS脚本异常！信息:{}", ExceptionUtil.getSimpleMessage(ex));
        }
        try {
            return JSONArray.parseArray(JSON.toJSONString(result, SerializerFeature.WriteMapNullValue));
        } catch (Exception exx) {
            try {
                return JSONArray.parseArray(StrUtil.toString(result));
            } catch (Exception exxx) {
                try {
                    return JSON.parseObject(JSON.toJSONString(result, SerializerFeature.WriteMapNullValue), JSONObject.class);
                } catch (Exception exxxx) {
                    try {
                        return JSON.parseObject(StrUtil.toString(result), JSONObject.class);
                    } catch (Exception exxxxx) {
                        return result;
                    }
                }
            }
        }
    }

    @Override
    public SysUser queryNodeUser(String wfId) {
        List<Task> task = taskService.createTaskQuery().processInstanceId(wfId).list();
        String taskId = task.get(0).getId();

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(wfId)
                .includeProcessVariables().singleResult();
        String processDefinitionKey = historicProcessInstance.getProcessDefinitionKey();
        ProcessTemplates processTemplates = processTemplateService.getById(processDefinitionKey.replace(PROCESS_PREFIX, ""));

        Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();
        JSONObject jsonObject = (JSONObject) processVariables.get(FORM_VAR);
        String process = processTemplates.getProcess();
        ChildNode childNode = JSONObject.parseObject(process, new TypeReference<ChildNode>() {
        });
        ChildNode currentNode = null;
        if (StringUtils.isNotBlank(taskId)) {
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
            currentNode = BpmnModelUtils.getChildNode(childNode, historicTaskInstance.getTaskDefinitionKey());
            List<FormOperates> formPerms = currentNode.getProps().getFormPerms();
            if (CollectionUtils.isNotEmpty(formPerms)) {
                Iterator<FormOperates> iterator = formPerms.iterator();
                while (iterator.hasNext()) {
                    FormOperates next = iterator.next();
                    if ("H".equals(next.getPerm())) {
                        iterator.remove();
                        if (jsonObject != null) {
                            jsonObject.remove(next.getId());
                        }
                    }
                }
            }
        }
        return userService.getById(currentNode.getProps().getAssignedUser().get(0).getUserid());
    }



    @Override
    public void updateRecordStatus(String formName, Long recordId, Integer wfStatus,List<HistoricActivityInstance> list) {
        for (CallBackApprovalResults callBackApprovalResult : callBackApprovalResults) {
            callBackApprovalResult.updateRecordStatus(formName, recordId, wfStatus,list);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public synchronized R batchApproval(List<HandleDataListDTO> handleDataListDTOList) {

        R result = batchApprovalCheck(handleDataListDTOList);
        if (!result.isSuccess()) {
            return result;
        }


        for (HandleDataListDTO item : handleDataListDTOList) {
            WorkFlow workFlow = workFlowService.queryWf(item.getRecordId(), item.getFormName());
            List<Task> task = taskService.createTaskQuery().processInstanceId(workFlow.getWfId()).list();
            if (CollectionUtils.isEmpty(task)) {
                return R.fail(item.getRecordId() + "任务不存在或已处理！");
            }

            HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(workFlow.getWfId())
                    .includeProcessVariables().singleResult();
            Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();

            JSONObject jsonObject = (JSONObject) processVariables.get(FORM_VAR);
            HandleDataDTO handleDataDTO = new HandleDataDTO();
            handleDataDTO.setFormData(jsonObject);
            handleDataDTO.setComments(item.getComments());
            handleDataDTO.setHandleType(item.getHandleType());
            handleDataDTO.setTaskId(task.get(0).getId());
            handleDataDTO.setProcessInstanceId(workFlow.getWfId());
            handleDataDTO.setCurrentUserInfo(item.getCurrentUserInfo());


            R handle = handle(handleDataDTO);
            if (!handle.isSuccess()) {
                return R.fail(item.getRecordId() + handle.getErrorMessage());
            }
        }

        return R.ok(true);
    }


    private String getCurrentName(String processInstanceId, Boolean flag, String processDefinitionId) {
        if (flag) {
            return "流程已结束";
        }
        List<String> activeActivityIds = runtimeService.getActiveActivityIds(processInstanceId);
        String activityId = activeActivityIds.get(0);
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        FlowElement flowElement = bpmnModel.getMainProcess().getFlowElement(activityId);
        return flowElement.getName();
    }

    /**
     * 校验加签人
     *
     * @param taskId 任务id
     * @param userId 加签人id
     * @return
     * @throws Exception
     */
    private R validateMultiAddUser(String taskId, String userId) {

        List<Task> list = queryTask(taskId);
        if (CollectionUtils.isEmpty(list)) {
            return R.fail("任务不存在");
        }
        return R.ok(list.stream().anyMatch(l -> userId.equals(l.getAssignee())));
    }

    /**
     * 撤销校验
     *
     * @param processInstanceId 实例id
     * @param userId            用户id
     * @return
     */
    private boolean validateRootUser(String processInstanceId, String userId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId)
                .includeProcessVariables().singleResult();
        return userId.equals(historicProcessInstance.getStartUserId());
    }

    /**
     * 根据任务id查询信息
     *
     * @param taskId 任务id
     * @return
     */
    private List<Task> queryTask(String taskId) {
        if (StringUtils.isBlank(taskId)) {
            return Collections.emptyList();
        }
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        return taskService.createTaskQuery()
                .processInstanceId(task.getProcessInstanceId())
                .taskDefinitionKey(task.getTaskDefinitionKey()).list();

    }

    private List<MultiVO> buildMultiData(List<Task> list, String taskId) {
        Iterator<Task> iterator = list.iterator();
        List<MultiVO> multiVOList = new ArrayList<>();
        while (iterator.hasNext()) {
            Task next = iterator.next();
            if (!taskId.equals(next.getId())) {
                MultiVO multiVO = new MultiVO();
                multiVO.setTaskId(next.getId());
                multiVO.setProcessInstanceId(next.getProcessInstanceId());
                multiVO.setExecutionId(next.getExecutionId());
                multiVO.setUserId(next.getAssignee());
                multiVOList.add(multiVO);
            }
        }
        List<String> userIds = list.stream().map(Task::getAssignee).collect(Collectors.toList());
        List<Long> userId = userIds.stream().map(l -> Long.parseLong(l.trim())).collect(Collectors.toList());
        List<SysUser> users = userService.queryUserList(userId);
        Map<Long, String> userMap = users.stream().collect(Collectors.toMap(SysUser::getId, SysUser::getName));
        multiVOList.stream().forEach(item -> {
            if (userMap.containsKey(Long.parseLong(item.getUserId()))) {
                item.setUserName(userMap.get(Long.parseLong(item.getUserId())));
            }
        });
        return multiVOList;
    }

    private HandleDataVO buildInstanceInfo(String processInstanceId, String taskId) {

        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId)
                .includeProcessVariables().singleResult();
        String processDefinitionKey = historicProcessInstance.getProcessDefinitionKey();
        ProcessTemplates processTemplates = processTemplateService.getById(processDefinitionKey.replace(PROCESS_PREFIX, ""));
        processTemplates.setLogo(processTemplates.getIcon());
        processTemplates.setFormId(processTemplates.getTemplateId());
        processTemplates.setFormName(processTemplates.getTemplateName());
        processTemplates.setProcessDefinitionId(historicProcessInstance.getProcessDefinitionId());

        HandleDataVO handleDataVO = new HandleDataVO();
        Map<String, Object> processVariables = historicProcessInstance.getProcessVariables();

        handleDataVO.setProcessInstanceId(historicProcessInstance.getId());
        JSONObject jsonObject = (JSONObject) processVariables.get(FORM_VAR);
        handleDataVO.setFormData(jsonObject);
        handleDataVO.setBusinessStatus(MapUtil.getStr(processVariables, PROCESS_STATUS));
        String process = processTemplates.getProcess();
        ChildNode childNode = JSONObject.parseObject(process, new TypeReference<ChildNode>() {
        });
        SettingsInfo settingsInfo = JSONObject.parseObject(processTemplates.getSettings(), new TypeReference<SettingsInfo>() {
        });
        Boolean sign = settingsInfo.getSign();
        ChildNode currentNode = null;
        if (StringUtils.isNotBlank(taskId)) {
            HistoricTaskInstance historicTaskInstance = historyService.createHistoricTaskInstanceQuery().taskId(taskId).singleResult();
            currentNode = BpmnModelUtils.getChildNode(childNode, historicTaskInstance.getTaskDefinitionKey());
            List<FormOperates> formPerms = currentNode.getProps().getFormPerms();
            if (CollectionUtils.isNotEmpty(formPerms)) {
                Iterator<FormOperates> iterator = formPerms.iterator();
                while (iterator.hasNext()) {
                    FormOperates next = iterator.next();
                    if ("H".equals(next.getPerm())) {
                        iterator.remove();
                        if (jsonObject != null) {
                            jsonObject.remove(next.getId());
                        }
                    }
                }
            }
            handleDataVO.setCurrentNode(currentNode);
            handleDataVO.setTaskId(taskId);
        }

        if (sign) {
            handleDataVO.setSignFlag(true);
        }
        if (StringUtils.isNotBlank(taskId)) {
            if (currentNode != null) {
                if (currentNode.getProps().getSign()) {
                    handleDataVO.setSignFlag(true);
                } else {
                    handleDataVO.setSignFlag(false);
                }
            }
        }

        buildNode(handleDataVO, historicProcessInstance);
        handleDataVO.setProcessTemplates(processTemplates);

        return handleDataVO;
    }

    private void buildNode(HandleDataVO handleDataVO, HistoricProcessInstance historicProcessInstance) {
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(historicProcessInstance.getId()).list();
        Map<String, List<HistoricActivityInstance>> historicActivityInstanceMap = list.stream().collect(
                Collectors.groupingBy(HistoricActivityInstance::getActivityId));

        Process mainProcess = repositoryService.getBpmnModel(
                historicProcessInstance.getProcessDefinitionId()).getMainProcess();
        Collection<FlowElement> flowElements = mainProcess.getFlowElements();

        List<Comment> processInstanceComments = taskService.getProcessInstanceComments(historicProcessInstance.getId());
        List<Attachment> processInstanceAttachments = taskService.getProcessInstanceAttachments(historicProcessInstance.getId());

        List<String> runningList = new ArrayList<>();
        List<String> endList = new ArrayList<>();
        List<String> noTakeList = new ArrayList<>();
        List<TaskDetailVO> nodeList = new ArrayList<>();
        Map<String, List<TaskDetailVO>> deatailMap = new HashMap<>();
        flowElements.stream().forEach(flowElement -> {
            List<TaskDetailVO> detailVOList = new ArrayList<>();
            List<HistoricActivityInstance> historicActivityInstanceList = historicActivityInstanceMap.get(flowElement.getId());
            if (CollectionUtils.isNotEmpty(historicActivityInstanceList)) {
                historicActivityInstanceList.stream().forEach(historicActivityInstance -> {
                    List<TaskDetailVO> voList = CollectionUtils.isEmpty(deatailMap.get(
                            historicActivityInstance.getActivityId())) ? new ArrayList<>() :
                            deatailMap.get(historicActivityInstance.getActivityId());
                    if (historicActivityInstance.getEndTime() != null) {
                        if (START_EVENT.equalsIgnoreCase(historicActivityInstance.getActivityType())
                                || END_EVENT.equalsIgnoreCase(historicActivityInstance.getActivityType())) {
                            TaskDetailVO taskDetailVO = buildTaskDetail(historicActivityInstance);
                            detailVOList.add(taskDetailVO);
                            deatailMap.put(historicActivityInstance.getActivityId(), detailVOList);
                            endList.add(historicActivityInstance.getActivityId());
                        } else if (USER_TASK.equalsIgnoreCase(historicActivityInstance.getActivityType())) {
                            List<HistoricActivityInstance> activityInstanceList = list.stream().filter(
                                    h -> h.getActivityId().equals(historicActivityInstance.getActivityId())
                                            && h.getEndTime() != null).collect(Collectors.toList());
                            collectUserTaskInfo(processInstanceComments, processInstanceAttachments,
                                    historicActivityInstance, historicProcessInstance, voList, activityInstanceList);
                            deatailMap.put(historicActivityInstance.getActivityId(), voList);
                            endList.add(historicActivityInstance.getActivityId());
                            nodeList.addAll(voList);
                        } else if (SERVICE_TASK.equalsIgnoreCase(historicActivityInstance.getActivityType())) {

                        }
                    } else {
                        if (USER_TASK.equalsIgnoreCase(historicActivityInstance.getActivityType())) {
                            List<HistoricActivityInstance> activityInstanceList = list.stream().filter(
                                    h -> h.getActivityId().equals(historicActivityInstance.getActivityId())
                                            && h.getEndTime() == null).collect(Collectors.toList());
                            collectUserTaskInfo(processInstanceComments, processInstanceAttachments,
                                    historicActivityInstance, historicProcessInstance, voList, activityInstanceList);
                            deatailMap.put(historicActivityInstance.getActivityId(), voList);
                            nodeList.addAll(voList);
                            if (endList.contains(historicActivityInstance.getActivityId())) {
                                endList.remove(historicActivityInstance.getActivityId());
                                runningList.add(historicActivityInstance.getActivityId());
                            } else {
                                runningList.add(historicActivityInstance.getActivityId());
                            }
                        } else if (SERVICE_TASK.equalsIgnoreCase(historicActivityInstance.getActivityType())) {

                        }
                    }
                });

            } else {
                noTakeList.add(flowElement.getId());
            }
        });
        handleDataVO.setRunningList(runningList);
        handleDataVO.setEndList(endList);
        handleDataVO.setNoTakeList(noTakeList);
        handleDataVO.setNodeList(nodeList.stream().distinct().collect(Collectors.toList()));
        handleDataVO.setDetailVOList(deatailMap);
    }


    private void collectUserTaskInfo(List<Comment> processInstanceComments,
                                     List<Attachment> processInstanceAttachments,
                                     HistoricActivityInstance historicActivityInstance,
                                     HistoricProcessInstance historicProcessInstance,
                                     List<TaskDetailVO> voList,
                                     List<HistoricActivityInstance> activityInstanceList) {
        activityInstanceList.stream().forEach(item -> {
            if (CollectionUtils.isNotEmpty(voList) && voList.stream().anyMatch(v -> item.getTaskId().equals(v.getTaskId()))) {
                return;
            }

            Comment signComment = processInstanceComments.stream().filter(h -> h.getTaskId()
                    .equals(historicActivityInstance.getTaskId()) && h.getType().equals("sign")).findFirst().orElse(null);
            List<Attachment> attachments = processInstanceAttachments.stream().filter(h -> h.getTaskId()
                    .equals(historicActivityInstance.getTaskId())).collect(Collectors.toList());
            List<Comment> options = processInstanceComments.stream().filter(h -> h.getTaskId()
                    .equals(historicActivityInstance.getTaskId())).collect(Collectors.toList());
            List<Comment> comments = processInstanceComments.stream().filter(h -> h.getTaskId()
                    .equals(historicActivityInstance.getTaskId()) && h.getType().equals("comments")).collect(Collectors.toList());
            TaskDetailVO taskDetailVO = buildTaskDetail(item);
            if (signComment != null) {
                taskDetailVO.setSignImage(signComment.getFullMessage());
            }
            buildAttachments(taskDetailVO, attachments);
            buildComment(taskDetailVO, comments);
            buildOption(taskDetailVO, options);
            buildUser(taskDetailVO, item, historicProcessInstance);
            voList.add(taskDetailVO);
        });
    }

    private TaskDetailVO buildTaskDetail(HistoricActivityInstance historicActivityInstance) {

        TaskDetailVO taskDetailVO = new TaskDetailVO();
        taskDetailVO.setTaskId(historicActivityInstance.getTaskId());
        taskDetailVO.setActivityId(historicActivityInstance.getActivityId());
        taskDetailVO.setName(historicActivityInstance.getActivityName());
        taskDetailVO.setCreateTime(historicActivityInstance.getStartTime());
        taskDetailVO.setEndTime(historicActivityInstance.getEndTime());

        return taskDetailVO;
    }

    private void buildAttachments(TaskDetailVO taskDetailVO, List<Attachment> attachments) {
        if (CollectionUtils.isNotEmpty(attachments)) {
            List<AttachmentVO> attachmentVOList = new ArrayList<>();
            attachments.stream().forEach(attachment -> {
                AttachmentVO attachmentVO = new AttachmentVO();
                attachmentVO.setId(attachment.getId());
                attachmentVO.setName(attachment.getName());
                attachmentVO.setUrl(attachment.getUrl());
                attachmentVOList.add(attachmentVO);
            });
            taskDetailVO.setAttachmentVOList(attachmentVOList);
        }
    }

    private void buildComment(TaskDetailVO taskDetailVO, List<Comment> comments) {
        if (CollectionUtils.isNotEmpty(comments)) {
            List<CommentVO> commentsVOList = new ArrayList<>();
            comments.stream().forEach(comment -> {
                CommentVO commentVO = new CommentVO();
                commentVO.setComments(comment.getFullMessage());
                commentVO.setUserId(comment.getUserId());
                commentVO.setCreateTime(comment.getTime());
                commentsVOList.add(commentVO);
            });
            List<String> userIds = commentsVOList.stream().map(CommentVO::getUserId).collect(Collectors.toList());
            List<Long> userId = userIds.stream().map(l -> Long.parseLong(l.trim())).collect(Collectors.toList());
            List<SysUser> users = userService.queryUserList(userId);
            Map<Long, String> userMap = users.stream().collect(Collectors.toMap(SysUser::getId, SysUser::getName));
            commentsVOList.stream().forEach(item -> {
                if (userMap.containsKey(Long.parseLong(item.getUserId()))) {
                    item.setUserName(userMap.get(Long.parseLong(item.getUserId())));
                }
            });
            taskDetailVO.setCommentVOList(commentsVOList);
        }
    }

    private void buildOption(TaskDetailVO taskDetailVO, List<Comment> options) {
        if (CollectionUtils.isNotEmpty(options)) {
            List<OptionVO> optionVOList = new ArrayList<>();
            options.stream().forEach(option -> {
                CommentEntityImpl comment = new CommentEntityImpl();
                if (option instanceof CommentEntityImpl) {
                    comment = (CommentEntityImpl) option;
                }
                OptionVO optionVO = new OptionVO();
                optionVO.setComments(comment.getMessage());
                optionVO.setUserId(option.getUserId());
                optionVO.setCreateTime(option.getTime());
                optionVO.setResult(option.getType());
                optionVOList.add(optionVO);
            });
            taskDetailVO.setOptionVOList(optionVOList);
        }
    }

    private void buildUser(TaskDetailVO taskDetailVO,
                           HistoricActivityInstance historicActivityInstance,
                           HistoricProcessInstance historicProcessInstance) {

        Long userId = null;
        if (null == historicActivityInstance.getAssignee()) {
            if ("root".equals(historicActivityInstance.getActivityId())) {
                userId = Long.valueOf(historicProcessInstance.getStartUserId());
            }
        } else {
            userId = Long.valueOf(historicActivityInstance.getAssignee());
        }

        if (userId == null) {
            return;
        }
        SysUser user = userService.queryUserList(Collections.singletonList(userId)).stream().findFirst().orElse(null);
        if (Objects.isNull(user)) {
            return;
        }
        CurrentUser currentUser = new CurrentUser();
        currentUser.setUserid(String.valueOf(user.getId()));
        currentUser.setName(user.getName());
        currentUser.setAvatar(user.getAvatar());
        taskDetailVO.setHandleUser(currentUser);
    }

    private void saveWorkFlow(StartProcessInstanceDTO startProcessInstanceDTO, ProcessInstance processInstance,
                              String processInstanceId, Long taskId) {
        JwtUserInfo currentUser = JwtUserInfo.fromHeader();
        WorkFlow workFlow = new WorkFlow();
        workFlow.setWfId(processInstance.getId());
        workFlow.setFormName(startProcessInstanceDTO.getFormName());
        workFlow.setRecordId(startProcessInstanceDTO.getRecordId());
        workFlow.setTaskId(taskId);
        workFlow.setProcessInstanceId(processInstanceId);
        workFlow.setWfStatus(Integer.valueOf(BUSINESS_STATUS_1));
        workFlow.setCreateTime(new Date());
        workFlow.setCreateUser(currentUser.getUserId());
        workFlow.setLastUpdateTime(new Date());
        workFlow.setLastUpdateUser(currentUser.getUserId());
        workFlowService.save(workFlow);
    }

    private R checkApprove(List<StartProcessInstanceDTO> startList) {

        if (CollectionUtils.isEmpty(startList)) {
            return R.fail("任务不存在！");
        }
        List<Long> recordIds = startList.stream().map(StartProcessInstanceDTO::getRecordId).collect(Collectors.toList());
        List<String> formNames = startList.stream().map(StartProcessInstanceDTO::getFormName).collect(Collectors.toList());
        List<WorkFlow> workFlows = workFlowService.queryWfList(recordIds, formNames);
        if (CollectionUtils.isEmpty(workFlows)) {
            return R.ok(true);
        }
        if (workFlows.stream().anyMatch(workFlow -> BUSINESS_STATUS_1.equals(workFlow.getWfStatus().toString()))) {
            return R.fail("审批中，请勿重复提交！");
        }
        return R.ok(true);
    }

    private R<List<Long>> batchApprovalCheck(List<HandleDataListDTO> handleDataListDTOList) {

        List<Long> recordIds = handleDataListDTOList.stream().map(HandleDataListDTO::getRecordId).collect(Collectors.toList());
        List<String> formNames = handleDataListDTOList.stream().map(HandleDataListDTO::getFormName).collect(Collectors.toList());
        List<WorkFlow> workFlows = workFlowService.queryWfList(recordIds, formNames);
        if (CollectionUtils.isEmpty(workFlows)) {
            return R.fail("任务不存在！");
        }
        List<Long> ids = workFlows.stream().filter(
                workFlow -> !BUSINESS_STATUS_1.equals(workFlow.getWfStatus().toString())).map(WorkFlow::getRecordId).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(ids)) {
            return R.fail(ids + "任务已办结");
        }

        return R.ok(true);

    }


}
